package cn.ac.ict.fpevaluation.fusion;

import cn.ac.ict.fpevaluation.bean.*;
import cn.ac.ict.fpevaluation.evaluation.WifiEvaluationByMagnetic;
import cn.ac.ict.fpevaluation.fastdtw.dtw.TimeWarpInfo;
import cn.ac.ict.fpevaluation.fastdtw.dtw.WarpPath;
import cn.ac.ict.fpevaluation.fastdtw.test.FastDTWTest;
import cn.ac.ict.fpevaluation.fastdtw.test.MagneticSample;
import cn.ac.ict.fpevaluation.fastdtw.timeseries.TimeSeriesPoint;
import cn.ac.ict.fpevaluation.utils.DataOperationToDiskUtils;
import cn.ac.ict.fpevaluation.utils.MagneticHelper;
import cn.ac.ict.fpevaluation.utils.WifiHelper;

import java.util.*;

/**
 * 将同一条路径的多条轨迹进行融合,目的在于增强系统鲁棒性和针对不同用户和手机的轨迹可区分度.
 */
public class FusionWifi {

    public AtomTrajectoryBean fusion(List<AtomTrajectoryBean> trajs) {
        if (trajs == null || trajs.size() == 0) return null;
        if (trajs.size() == 1) return trajs.get(0);
        AtomTrajectoryBean traj = trajs.get(0);
        for (int i = 1; i < trajs.size(); i++) {
            traj = remedyWifiByMagnetic(traj, trajs.get(i));
        }
        return traj;
    }


    public AtomTrajectoryBean remedyWifiByMagnetic(AtomTrajectoryBean traj1, AtomTrajectoryBean traj2) {
        ArrayList<MagneticSample> mag1 = traj2MagneticList(traj1);
        ArrayList<MagneticSample> mag2 = traj2MagneticList(traj2);

        AtomTrajectoryBean res = new AtomTrajectoryBean();
        res.setEndX((traj1.getEndX() + traj2.getEndX()) / 2);
        res.setEndY((traj1.getEndY() + traj2.getEndY()) / 2);
        res.setStartX((traj1.getStartX() + traj2.getStartX()) / 2);
        res.setStartY((traj1.getStartY() + traj2.getStartY()) / 2);
        res.setName(traj1.getName());
        res.setOrientation(fusionOritation(traj1.getOrientation(), traj2.getOrientation()));
        res.setPhoneBrand(traj1.getPhoneBrand());

        Map<Integer, List<APBean>> maps = new HashMap<>();
        List<StepBean> steps = new ArrayList<>();

        TimeWarpInfo t = new FastDTWTest().calDistance(gettsArray(mag1), gettsArray(mag2), 3);
        WarpPath path = t.getPath();
        for (int i = 0; i < mag1.size(); i++) {
            ArrayList<Object> matchingJ = path.getMatchingIndexesForI(i);
            int stepCountForI = getStepCountByMag(i, mag1);

            List<APBean> apForI = traj1.getAps().get(stepCountForI);
            List<List<APBean>> apForJs = new ArrayList<>();
            for (Object j : matchingJ) {
                List<APBean> apBeans = traj2.getAps().get(getStepCountByMag((int) (j), mag2));
                apForJs.add(apBeans);
            }

            List<APBean> apBeans = remedyRss(apForI, apForJs);
            if (apBeans != null) {
                maps.put(stepCountForI, apBeans);
            }
            if (!steps.contains(traj1.getStepList().get(stepCountForI))) {
                steps.add(traj1.getStepList().get(stepCountForI));
            }
        }

        List<List<APBean>> aps = new ArrayList<>();
        for (Integer integer : maps.keySet()) {
            aps.add(maps.get(integer));
        }
        res.setAps(aps);
        res.setStepList(steps);
        res.setStepLength(steps.size());

        return res;

    }


    private static StepBean fusionStep(StepBean a, List<StepBean> b) {
        //TODO FUSION MAGNETIC
        return null;
    }

    private static List<Double> fusionOritation(List<Double> oritations1, List<Double> oritations2) {
        int i = 0, size1 = oritations1.size(), size2 = oritations2.size();
        List<Double> res = new ArrayList<Double>();
        List<Double> temp = new ArrayList<Double>();
        while (i < size1 && i < size2) {
            temp.add((oritations1.get(i) + oritations2.get(i)) / 2);
            i++;
        }

        if (i == size1) {
            while (i < size2) res.add(oritations2.get(i));
        }

        if (i == size2) {
            while (i < size1) res.add(oritations1.get(i));
        }

        res.addAll(temp);

        return res;
    }

    private int getStepCountByMag(int i, ArrayList<MagneticSample> samples) {
        return samples.get(i).getStepCount();
    }


    private List<APBean> remedyRss(List<APBean> apForI, List<List<APBean>> apForJs) {

        HashMap<String, Double> map = new HashMap<>(); //<MAC, RSS>
        HashMap<String, String> ssid = new HashMap<>(); //<MAC, SSID>
        apForI.forEach(item -> {
            String mac = item.getMAC();
            ssid.put(mac, item.getSSID());
            if (!map.containsKey(mac)) {
                map.put(mac, item.getRSS());
            } else {
                map.put(item.getMAC(), (map.get(mac) + item.getRSS()) / 2);
            }
        });

        for (List<APBean> apForJ : apForJs) {
            for (APBean apBean : apForJ) {
                String mac = apBean.getMAC();
                ssid.put(mac, apBean.getSSID());
                if (!map.containsKey(mac)) {
                    map.put(mac, apBean.getRSS());
                } else {
                    map.put(mac, (map.get(mac) + apBean.getRSS()) / 2);
                }
            }
        }

        List<APBean> res = new ArrayList<>();
        for (String s : map.keySet()) {
            res.add(new APBean(map.get(s), s, ssid.get(s)));
        }
        return res;

    }

    protected ArrayList<MagneticSample> traj2MagneticList(AtomTrajectoryBean data) {
        // 把AtomTrajectoryBean变成Sample集合
        ArrayList<MagneticSample> sampleLists = new ArrayList<>();
        int stepCount = 0;
        for (StepBean step : data.getStepList()) {
            int size = step.getxMagnetic().size();
            for (int j = 0; j < size; j++) {
                MagneticSample sample = new MagneticSample();
                sample.setMagXOrigin(step.getxMagnetic().get(j));
                sample.setMagYOrigin(step.getyMagnetic().get(j));
                sample.setMagZOrigin(step.getzMagnetic().get(j));
                sample.setStepCount(stepCount);
                sampleLists.add(sample);
            }
            stepCount++;
        }

        return sampleLists;
    }

    private static ArrayList<Object> gettsArray(ArrayList<?> samplelist) {
        ArrayList<Object> tsArray = new ArrayList<Object>();

        for (int i = 0; i < samplelist.size(); i++) {
            final ArrayList<Double> currentLineValues = new ArrayList<Double>();
            Object sample = samplelist.get(i);
            if (sample instanceof MagneticSample) {
                currentLineValues.add(((MagneticSample) sample).getMagXOrigin());
                currentLineValues.add(((MagneticSample) sample).getMagYOrigin());
                currentLineValues.add(((MagneticSample) sample).getMagZOrigin());
            }

            final TimeSeriesPoint readings = new TimeSeriesPoint(currentLineValues.subList(0, currentLineValues.size()));

            tsArray.add(readings);
        }
        return tsArray;
    }

}
